package com.org.utils;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameRecorder;
import org.bytedeco.javacv.Java2DFrameConverter;

import javax.sound.sampled.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class VideoRecorder {

    private ScheduledThreadPoolExecutor screenTimer;

    private Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
    private final Rectangle rectangle=new Rectangle(screenSize.width,screenSize.height);

    private FFmpegFrameRecorder recorder;

    private Robot robot;

    private ScheduledThreadPoolExecutor exec;
    private TargetDataLine line;
    private AudioFormat audioFormat;
    private DataLine.Info dataLineInfo;
    private boolean isHaveDevice=true;
    private long startTime=0;
    private  long videoTS=0;
    private long pauseTime=0;
    private double frameRate=5;

    public VideoRecorder(String fileName,boolean isHaveDevice){
        recorder=new FFmpegFrameRecorder(fileName+".mp4",screenSize.width,screenSize.height);

        recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);
        recorder.setFormat("mp4");
        recorder.setSampleRate(44100);
        recorder.setFrameRate(frameRate);
        recorder.setVideoQuality(0);
        recorder.setVideoOption("crf","23");
        recorder.setVideoBitrate(1000000);

        recorder.setVideoOption("preset","slow");
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setAudioChannels(2);
        recorder.setAudioOption("crf","0");
        recorder.setAudioQuality(0);
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);

        try {
            robot=new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
        try {
            recorder.start();
        } catch (Exception e) {
            System.out.println("**********************************");
        }

        this.isHaveDevice=isHaveDevice;
    }

    public void start(){
        if (startTime==0){
            startTime=System.currentTimeMillis();
        }
        if (pauseTime==0){
            pauseTime=System.currentTimeMillis();
        }
        if (isHaveDevice){
            new Thread(new Runnable() {
                public void run() {
                    capture();
                }
            }).start();
        }
        screenTimer=new ScheduledThreadPoolExecutor(1);
        screenTimer.scheduleAtFixedRate(new Runnable() {
            public void run() {
                BufferedImage screenCapture=robot.createScreenCapture(rectangle);
                BufferedImage videoImg=new BufferedImage(screenSize.width,screenSize.height,BufferedImage.TYPE_3BYTE_BGR);
                Graphics2D videoGraphics=videoImg.createGraphics();
                videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_DISABLE);
                videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_SPEED);
                videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_SPEED);
                videoGraphics.drawImage(screenCapture,0,0,null);
                Java2DFrameConverter java2DConverter=new Java2DFrameConverter();
                Frame frame=java2DConverter.convert(videoImg);


                try {
                    videoTS=1000L*(System.currentTimeMillis()-startTime-(System.currentTimeMillis()-pauseTime));
                    if (videoTS>recorder.getTimestamp()){
                        recorder.setTimestamp(videoTS);
                    }
                    recorder.record(frame);
                } catch (FrameRecorder.Exception e) {
                    e.printStackTrace();
                }
                videoGraphics.dispose();
                videoGraphics=null;
                videoImg.flush();
                videoImg=null;
                java2DConverter=null;
                screenCapture.flush();
                screenCapture=null;
            }
        },(int)(1000/frameRate),(int)(1000/frameRate), TimeUnit.MILLISECONDS);
    }

    public void capture(){
        audioFormat=new AudioFormat(44100.0F,16,2,true,false);
        dataLineInfo=new DataLine.Info(TargetDataLine.class,audioFormat);

        try {
            line=(TargetDataLine) AudioSystem.getLine(dataLineInfo);
        } catch (LineUnavailableException e) {
            System.out.println("###########################");
        }
        try {
            line.open(audioFormat);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
        line.start();

        final int sampleRate=(int)audioFormat.getSampleRate();
        final int numChanndls=audioFormat.getChannels();
        int audioBufferSize=sampleRate*numChanndls;
        final byte[] audioBytes=new byte[audioBufferSize];
        exec=new ScheduledThreadPoolExecutor(1);
        exec.scheduleAtFixedRate(new Runnable() {
            public void run() {
                try {
                    int nBytesRead=line.read(audioBytes,0,line.available());
                    int nSamplesRead=nBytesRead/2;
                    short[] samples=new short[nSamplesRead];

                    ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
                    ShortBuffer sBuff=ShortBuffer.wrap(samples,0,nSamplesRead);
                    recorder.recordSamples(sampleRate,numChanndls,sBuff);
                } catch (FrameRecorder.Exception e) {
                    e.printStackTrace();
                }

            }
        },(int)(1000/frameRate),(int)(1000/frameRate),TimeUnit.MILLISECONDS);

    }

    public void stop(){
        if (null!=screenTimer){
            screenTimer.shutdown();
        }

        try {
            recorder.stop();
            recorder.release();
            recorder.close();
            screenTimer=null;
            if (isHaveDevice){
                if (null!=exec){
                    exec.shutdownNow();
                }
                if (null!=line){
                    line.stop();
                    line.close();
                }
                dataLineInfo=null;
                audioFormat=null;
            }
        } catch (FrameRecorder.Exception e) {
            e.printStackTrace();
        }
    }

    public void pause(){
        screenTimer.shutdownNow();
        screenTimer=null;
        if (isHaveDevice){
            exec.shutdownNow();
            exec=null;
            line.stop();
            line.close();
            dataLineInfo=null;
            audioFormat=null;
            line=null;
        }
        pauseTime=System.currentTimeMillis();
    }



}
